import blue
import uthread
import sys
import _weakref
import log
import uiconst
import uiutil
import uicls
SCROLLMARGIN = 0
MINCOLUMNWIDTH = 24
VERSION = uiconst.SCROLLVERSION
TABMARGIN = 6

class ScrollCore(uicls.Container):
    __guid__ = 'uicls.ScrollCore'
    default_name = 'scroll'
    default_align = uiconst.TOALL
    default_pos = (0, 0, 0, 0)
    default_multiSelect = 1
    default_stickToBottom = 0
    default_smartSort = 0
    default_id = None
    default_state = uiconst.UI_NORMAL
    headerFontSize = 10
    scrollEnabled = True

    def ApplyAttributes(self, attributes):
        uicls.Container.ApplyAttributes(self, attributes)
        self.sr.maincontainer = uicls.Container(parent=self, name='maincontainer', padding=(1, 1, 1, 1), clipChildren=True)
        self.sr.scrollHeaders = uicls.Container(name='scrollHeaders', parent=self.sr.maincontainer, height=18, state=uiconst.UI_HIDDEN, align=uiconst.TOTOP)
        self.sr.clipper = uicls.Container(name='__clipper', align=uiconst.TOALL, parent=self.sr.maincontainer, clipChildren=True)
        self.sr.clipper.OnResize = self.OnClipperResize
        self.sr.content = uicls.Container(name='__content', align=uiconst.TOALL, parent=self.sr.clipper, state=uiconst.UI_NORMAL)
        self.Release()
        self.updatePosTasklet = None
        self.multiSelect = attributes.get('multiSelect', self.default_multiSelect)
        self.stickToBottom = attributes.get('stickToBottom', self.default_stickToBottom)
        self.smartSort = attributes.get('smartSort', self.default_smartSort)
        self.sr.id = attributes.get('id', self.default_id)
        self.subSortBy = attributes.get('subSortBy', None)
        self.sr.selfProxy = _weakref.proxy(self)
        self.Prepare_()



    def Prepare_(self):
        self.Prepare_ActiveFrame_()
        self.Prepare_Underlay_()
        self.Prepare_ScrollControls_()



    def Prepare_Underlay_(self):
        self.sr.underlay = uicls.Frame(name='__underlay', color=(0.0, 0.0, 0.0, 0.5), frameConst=uiconst.FRAME_FILLED_CORNER0, parent=self)



    def Prepare_ActiveFrame_(self):
        self.sr.activeframe = uicls.Frame(name='__activeframe', color=(1.0, 1.0, 1.0, 0.5), frameConst=uiconst.FRAME_BORDER1_SHADOW_CORNER0, parent=self, state=uiconst.UI_HIDDEN)



    def Prepare_ScrollControls_(self):
        self.sr.scrollcontrols = uicls.ScrollControls(name='__scrollcontrols', parent=self.sr.maincontainer, align=uiconst.TORIGHT, pos=(0, 0, 12, 0), state=uiconst.UI_HIDDEN, idx=0)
        self.sr.scrollcontrols.Startup(self)



    def RemoveActiveFrame(self, *args):
        if uiutil.GetAttrs(self, 'sr', 'activeframe'):
            self.sr.activeframe.color.a = 0.0



    def HideBackground(self, alwaysHidden = 0):
        if uiutil.GetAttrs(self, 'sr', 'underlay'):
            self.sr.underlay.SetAlpha(0.0)
        self.RemoveActiveFrame()
        if alwaysHidden:
            self.SetNoBackgroundFlag(alwaysHidden)


    HideUnderLay = HideBackground

    def SetNoBackgroundFlag(self, hide = 0):
        self.noBackground = hide



    def Release(self):
        self.isTabStop = 1
        self._loadedonce = 0
        self._loading = 0
        self._position = 0
        self._clipperHeight = 0
        self._totalHeight = 0
        self._fixedEntryHeight = None
        self._customColumnWidths = None
        self.sr.activeframe = None
        self.sr.scaleLine = None
        self.sr.columnHilite = None
        self.sr.cache = {}
        self.sr.headers = []
        self.sr.widthToHeaders = {}
        self.sr.tabs = []
        self.sr.nodes = []
        self.sr.overlays = []
        self.sr.underlays = []
        self.sr.fixedColumns = {}
        self.sr.maxDefaultColumns = {}
        self.sr.minColumnWidth = {}
        self.sr.defaultColumnWidth = {}
        self.sr.notSortableColumns = []
        self.sr.fromTopByIdx = {}
        self.sr.ignoreTabTrimming = 0
        self.sr.id = None
        self.sortBy = None
        self.sr.iconMargin = 0
        self.sr.hint = None
        self.scrollingRange = 0
        self.updatingSizes = 0
        self.hiliteSorted = 1
        self.reversedSort = 0
        self.debug = 0
        self.scrolling = 0
        self.scalingcol = 0
        self.refreshingColumns = 0
        self.lastDrawnColumns = None
        self.allowFilterColumns = 0
        self.lastSelected = None
        self.lastHeaders = None
        self.bumpHeaders = 1
        self.slimHeaders = 0
        self.newColumns = 0
        self.trimFast = 0
        self.refs = []
        self.lastCharReceivedAt = 0
        self.currChars = ''
        self.noBackground = 0
        self._ignoreSort = 0



    def _OnClose(self):
        uicls.Container._OnClose(self)
        for node in self.sr.nodes:
            node.panel = None
            node.scroll = None

        cache = self.sr.cache.values()
        self.sr.cache = None
        for set in cache:
            for each in set:
                each.Close()


        for each in self.sr.content.children:
            if hasattr(each, 'sr'):
                each.sr.node = None
            each.Close()

        self.sr.nodes = []
        self.OnDelete = None
        self.OnUpdatePosition = None
        self.OnColumnChanged = None
        self.GetHeaderMenu = None
        self.Resizing = None
        self.OnSelectionChange = None
        self.Confirm = None
        self.Up = None
        self.Down = None



    def OnSetFocus(self, *args):
        if (self and ((not self.destroyed) and (self.parent and (self.parent.name == 'inlines')))):
            if (self.parent.parent and self.parent.parent.sr.node):
                self.parent.parent.sr.node.scroll.ShowNodeIdx(self.parent.parent.sr.node.idx)
        if self.sr.activeframe:
            self.sr.activeframe.state = uiconst.UI_DISABLED



    def OnKillFocus(self, *args):
        if self.sr.activeframe:
            self.sr.activeframe.state = uiconst.UI_HIDDEN



    def RefreshHeaders(self, headers, tabs = []):
        self.sr.scrollHeaders.state = uiconst.UI_HIDDEN
        if self.sr.columnHilite:
            self.sr.columnHilite.state = uiconst.UI_HIDDEN
        uiutil.Flush(self.sr.scrollHeaders)
        for each in self.sr.clipper.children[:]:
            if (each.name == '__columnLine'):
                each.Close()

        self.sr.widthToHeaders = {}
        if not (len(headers) and (len(tabs) and (len(headers) == len(tabs)))):
            self.sr.headers = []
            self.sr.tabs = []
            self.sortBy = None
            return 
        self.sr.scrollHeaders.state = uiconst.UI_NORMAL
        uiutil.SetOrder(self.sr.scrollHeaders, 0)
        uicls.Line(parent=self.sr.scrollHeaders, align=uiconst.TOBOTTOM, color=(1.0, 1.0, 1.0, 0.25))
        i = 0
        totalWidth = 0
        maxTextHeight = 0
        for header in headers:
            width = self.sr.fixedColumns.get(header, None)
            if (width is None):
                if (len(headers) == 1):
                    width = 128
                else:
                    width = tabs[i]
                    if (i != 0):
                        width = (width - tabs[(i - 1)])
            sortdir = self.GetSmartSortDirection(header)
            self.sr.widthToHeaders[header] = totalWidth
            headerparent = uicls.ScrollColumnHeader(parent=self.sr.scrollHeaders, label=header)
            headerparent.SetAlign(uiconst.TOLEFT)
            headerparent.width = width
            headerparent.state = uiconst.UI_NORMAL
            headerparent.sr.column = header
            headerparent.sr.idx = i
            headerparent.sr.sortdir = sortdir
            headerparent.wr.scroll = self
            headerparent.sr.header = header
            headerparent.name = header
            headerparent.haveBar = 0
            if (headerparent.width < headerparent.sr.label.textwidth):
                headerparent.hint = header
            if ((header not in self.sr.fixedColumns) and self.sr.id):
                headerparent.width = width
                headerparent.haveBar = 1
                bar = uicls.Container(parent=headerparent, align=uiconst.TORIGHT, pos=(0, 0, 4, 0), state=uiconst.UI_NORMAL, idx=1)
                bar.name = 'scaler'
                bar.sr.column = header
                bar.OnMouseDown = self.StartScaleCol
                bar.OnMouseUp = self.EndScaleCol
                bar.OnMouseMove = self.ScalingCol
                bar.OnDblClick = (self.DblClickCol, bar)
                bar.cursor = 18
                bar.columnWidth = width
                uicls.Line(parent=bar, align=uiconst.TOLEFT, color=(1.0, 1.0, 1.0, 0.25))
            totalWidth += width
            if self.smartSort:
                if sortdir:
                    headerparent.ShowSortDirection(sortdir)
            if not (self.smartSort or self.allowFilterColumns):
                headerparent.GetMenu = None
            maxTextHeight = max(maxTextHeight, headerparent.sr.label.textheight)
            if ((not self.sr.ignoreTabTrimming) and self.sr.nodes):
                line = uicls.Line(parent=self.sr.clipper, align=uiconst.RELATIVE, color=(1.0, 1.0, 1.0, 0.125), name='__columnLine')
                line.width = 1
                line.height = uicore.desktop.height
                line.top = 1
                line.left = (totalWidth - 1)
            i += 1

        self.sr.scrollHeaders.height = (maxTextHeight + 3)
        self.sr.tabs = tabs
        self.lastDrawnColumns = headers



    def ShowHint(self, hint = None):
        if ((self.sr.hint is None) and hint):
            self.sr.hint = uicls.Label(text=hint, parent=self, align=uiconst.TOALL, pos=(16, 32, 16, 0), autowidth=0, autoheight=0, fontsize=24, idx=0)
        elif ((self.sr.hint is not None) and hint):
            self.sr.hint.text = hint
            self.sr.hint.state = uiconst.UI_DISABLED
        elif ((self.sr.hint is not None) and (not hint)):
            self.sr.hint.state = uiconst.UI_HIDDEN



    def StartScaleCol(self, sender, *args):
        if uicore.uilib.rightbtn:
            return 
        self.scalingcol = uicore.uilib.x
        if (self.sr.scaleLine is not None):
            self.sr.scaleLine.Close()
        (l, t, w, h,) = self.GetAbsolute()
        self.sr.scaleLine = uicls.Line(parent=self, align=uiconst.TOPLEFT, color=(1.0, 1.0, 1.0, 1.0), width=2, pos=(((uicore.uilib.x - l) - 1),
         1,
         0,
         h), idx=0)



    def ScalingCol(self, sender, *args):
        (l, t, w, h,) = self.GetAbsolute()
        minColumnWidth = self.sr.minColumnWidth.get(sender.sr.column, MINCOLUMNWIDTH)
        if (self.scalingcol and self.sr.scaleLine):
            self.sr.scaleLine.left = max(minColumnWidth, min((w - minColumnWidth), ((uicore.uilib.x - l) - 2)))



    def EndScaleCol(self, sender, *args):
        if (self.sr.scaleLine is not None):
            if (self.sr.id and (self.scalingcol != uicore.uilib.x)):
                currentSettings = settings.user.ui.Get(('columnWidths_%s' % VERSION), {})
                currentSettings.setdefault(self.sr.id, {})
                currentWidth = sender.columnWidth
                minColumnWidth = self.sr.minColumnWidth.get(sender.sr.column, MINCOLUMNWIDTH)
                newWidth = (currentWidth + (uicore.uilib.x - self.scalingcol))
                currentSettings[self.sr.id][sender.sr.column] = max(minColumnWidth, newWidth)
                settings.user.ui.Set(('columnWidths_%s' % VERSION), currentSettings)
                uthread.pool('VirtualScroll::EndScaleCol-->CheckTabStops', self.CheckTabStops, 'EndScaleCol')
            scaleLine = self.sr.scaleLine
            self.sr.scaleLine = None
            scaleLine.Close()
        self.scalingcol = 0



    def OnColumnChanged(self, *args):
        return 



    def OnNewHeaders(self, *args):
        return 



    def DblClickCol(self, sender, *args):
        self.ResetColumnWidth(sender.sr.column)



    def ShowNodeIdx(self, idx, toTop = 1):
        if self.scrollingRange:
            node = self.GetNode(idx)
            fromTop = self.GetHeightToIdx(idx)
            if (self._position > fromTop):
                portion = (fromTop / float(self.scrollingRange))
                self.ScrollToProportion(portion, 1)
            clipperWidth = self.GetContentWidth()
            nodeHeight = self.GetNodeHeight(node, clipperWidth)
            if ((self._position + self._clipperHeight) < (fromTop + nodeHeight)):
                portion = (((fromTop - self._clipperHeight) + nodeHeight) / float(self.scrollingRange))
                self.ScrollToProportion(portion, 1)
            self.UpdatePosition(fromWhere='ShowNodeIdx')



    def GetNodes(self, allowNone = False):
        ret = []
        for each in self.sr.nodes:
            if each.internalNodes:
                if allowNone:
                    ret += each.internalNodes
                else:
                    for internal in each.internalNodes:
                        if internal:
                            ret.append(internal)

            else:
                ret.append(each)

        return ret



    def SetSelected(self, idx):
        node = self.GetNode(idx)
        if node:
            self.SelectNode(node)
        self.ReportSelectionChange()



    def ActivateIdx(self, idx):
        node = self.GetNode(min(idx, (len(self.GetNodes()) - 1)))
        if node:
            self.SelectNode(node)
            self.ShowNodeIdx(node.idx)
        else:
            self.ReportSelectionChange()



    def _SelectNode(self, node):
        if (getattr(node, 'selectable', 1) == 0):
            return 
        node.selected = 1
        self.UpdateSelection(node)



    def _DeselectNode(self, node):
        node.selected = 0
        self.UpdateSelection(node)



    def SelectNode(self, node, multi = 0, subnode = None, checktoggle = 1):
        control = uicore.uilib.Key(uiconst.VK_CONTROL)
        shift = uicore.uilib.Key(uiconst.VK_SHIFT)
        selected = node.get('selected', 0)
        if not self.multiSelect:
            self.DeselectAll(0)
            if control:
                if not selected:
                    self._SelectNode(node)
            else:
                self._SelectNode(node)
        elif ((not control) and (not shift)):
            self.DeselectAll(0)
            self._SelectNode(node)
        elif (control and (not shift)):
            if not selected:
                self._SelectNode(node)
            else:
                self._DeselectNode(node)
        elif ((not control) and shift):
            if ((self.lastSelected is not None) and (self.lastSelected != node.idx)):
                self.DeselectAll(0)
                r = [self.lastSelected, node.idx]
                r.sort()
                for i in xrange(r[0], (r[1] + 1)):
                    _node = self.GetNode(i, checkInternalNodes=True)
                    if _node:
                        self._SelectNode(_node)

                self.ReportSelectionChange()
                return 
            self.DeselectAll(0)
            self._SelectNode(node)
        elif (control and shift):
            if ((self.lastSelected is not None) and (self.lastSelected != node.idx)):
                r = [self.lastSelected, node.idx]
                r.sort()
                for i in xrange(r[0], (r[1] + 1)):
                    _node = self.GetNode(i, checkInternalNodes=True)
                    if _node:
                        self._SelectNode(_node)

        else:
            self.DeselectAll(0)
            self._SelectNode(node)
        self.lastSelected = node.idx
        self.ReportSelectionChange()



    def ReportSelectionChange(self):
        self.OnSelectionChange(self.GetSelected())



    def OnSelectionChange(self, *args):
        return 



    def DeselectAll(self, report = 1, *args):
        for node in self.GetNodes():
            node.selected = 0
            self.UpdateSelection(node)

        if report:
            self.ReportSelectionChange()



    def SelectAll(self, *args):
        if not self.multiSelect:
            return 
        for node in self.GetNodes():
            if (getattr(node, 'selectable', 1) == 0):
                continue
            node.selected = 1
            self.UpdateSelection(node)

        self.ReportSelectionChange()



    def ToggleSelected(self, *args):
        for node in self.GetNodes():
            node.selected = (not node.get('selected', 0))
            self.UpdateSelection(node)

        self.ReportSelectionChange()



    def UpdateSelection(self, node):
        if node.panel:
            if node.panel.sr.selection:
                node.panel.sr.selection.state = [uiconst.UI_HIDDEN, uiconst.UI_DISABLED][node.selected]
            elif (node.selected and hasattr(node.panel, 'Select')):
                node.panel.Select()
            else:
                if ((not node.selected) and hasattr(node.panel, 'Deselect')):
                    node.panel.Deselect()



    def ClearSelection(self, *args):
        for node in self.GetNodes():
            node.selected = 0
            self.UpdateSelection(node)

        self.lastSelected = None
        self.ReportSelectionChange()



    def GetSelectedNodes(self, node, toggle = 0):
        if ((not node.get('selected', 0)) or toggle):
            self.SelectNode(node)
        sel = []
        for each in self.GetNodes():
            if each.get('selected', 0):
                sel.append(each)

        return sel



    def GetSelected(self):
        sel = []
        for each in self.GetNodes():
            if each.get('selected', 0):
                sel.append(each)

        return sel



    def GetSortBy(self):
        if self.smartSort:
            return 
        else:
            if self.sr.id:
                pr = settings.user.ui.Get(('scrollsortby_%s' % VERSION), {})
                if (self.sr.id in pr):
                    return pr[self.sr.id][0]
            return self.sortBy



    def GetSortDirection(self):
        if self.sr.id:
            pr = settings.user.ui.Get(('scrollsortby_%s' % VERSION), {})
            if (self.sr.id in pr):
                return pr[self.sr.id][1]
            return self.reversedSort



    def GetSmartSortDirection(self, column):
        if (self.sr.id and self.smartSort):
            if (column not in self.sr.notSortableColumns):
                pr = settings.user.ui.Get(('smartSortDirection_%s' % VERSION), {})
                if (self.sr.id in pr):
                    return pr[self.sr.id].get(column, 1)
                else:
                    return 
            return 1



    def ToggleSmartSortDirection(self, column):
        if (self.sr.id and self.smartSort):
            current = self.GetSmartSortDirection(column)
            new = [1, -1][(current == 1)]
            pr = settings.user.ui.Get(('smartSortDirection_%s' % VERSION), {})
            if (self.sr.id not in pr):
                pr[self.sr.id] = {}
            pr[self.sr.id][column] = new
            settings.user.ui.Set(('smartSortDirection_%s' % VERSION), pr)



    def GetSortValue(self, by, node, idx = None):
        ret = self._GetSortValue(by, node, idx)
        Deco = node.get('DecoSortValue', lambda x: x)
        return Deco(ret)



    def _GetSortValue(self, by, node, idx):
        val = (node.Get(('sort_' + by), None) or node.Get(('sort_' + by.replace('<br>', ' ')), None))
        if (val is not None):
            try:
                val = val.lower()
            except:
                sys.exc_clear()
            return val
        else:
            if (idx is not None):
                strings = (node.get('label', '') or node.get('text', '')).split('<t>')
                if (len(strings) > idx):
                    value = strings[idx].lower()
                    try:
                        value = uicore.font.DeTag(value)
                        isAU = (value.find('au') != -1)
                        isKM = (value.find('km') != -1)
                        value = float(value.replace('m\xb3', '').replace('isk', '').replace('km', '').replace('au', '').replace(',', '').replace(' ', ''))
                        if isAU:
                            value *= const.AU
                        elif isKM:
                            value *= 1000
                        return value
                    except:
                        sys.exc_clear()
                        rest = ''.join(strings[(idx + 1):])
                        return (value + rest)
                return 'aaa'
            val = node.Get(by, '-')
            try:
                val = val.lower()
            except:
                sys.exc_clear()
            return val



    def GetColumns(self):
        if (self.sr.id and (self.smartSort or self.allowFilterColumns)):
            if not self.sr.headers:
                return []
            else:
                orderedColumns = settings.user.ui.Get(('columnOrder_%s' % VERSION), {}).get(self.sr.id, self.sr.headers)
                notInOrdered = [ header for header in self.sr.headers if (header not in orderedColumns) ]
                headers = [ header for header in (orderedColumns + notInOrdered) if (header in self.sr.headers) ]
                hiddenColumns = settings.user.ui.Get(('filteredColumns_%s' % VERSION), {}).get(self.sr.id, [])
                allHiddenColumns = (hiddenColumns + settings.user.ui.Get(('filteredColumnsByDefault_%s' % VERSION), {}).get(self.sr.id, []))
                filterColumns = filter(lambda x: (x not in allHiddenColumns), headers)
                return filterColumns
            return self.sr.headers



    def GetHeaderMenu(self, label):
        m = []
        if self.smartSort:
            m += [(mls.UI_CMD_MAKEPRIMARY,
              self.MakePrimary,
              (label))]
        if (self.smartSort or self.allowFilterColumns):
            if (len(self.GetColumns()) > 1):
                m += [(('%s %s' % (mls.UI_GENERIC_HIDE, label)),
                  self.HideColumn,
                  (label))]
            m += self.GetShowColumnMenu()
        return m



    def GetShowColumnMenu(self):
        m = []
        for label in self.sr.headers:
            if (label not in self.GetColumns()):
                m.append((('%s %s' % (mls.UI_GENERIC_SHOW, label)),
                 self.ShowColumn,
                 (label)))

        if m:
            m.insert(0, None)
        return m



    def MakePrimary(self, label, update = 1):
        all = settings.user.ui.Get(('primaryColumn_%s' % VERSION), {})
        all[self.sr.id] = label
        settings.user.ui.Set(('primaryColumn_%s' % VERSION), all)
        if update:
            self.ChangeColumnOrder(label, 0)



    def GetPrimaryColumn(self):
        return settings.user.ui.Get(('primaryColumn_%s' % VERSION), {}).get(self.sr.id, None)



    def SetColumnsHiddenByDefault(self, columns, *args):
        if self.sr.id:
            filteredByDefault = settings.user.ui.Get(('filteredColumnsByDefault_%s' % VERSION), {})
            if (self.sr.id not in filteredByDefault):
                filteredByDefault[self.sr.id] = columns
                settings.user.ui.Set(('filteredColumnsByDefault_%s' % VERSION), filteredByDefault)



    def HideColumn(self, label):
        if self.sr.id:
            filtered = settings.user.ui.Get(('filteredColumns_%s' % VERSION), {})
            if (self.sr.id not in filtered):
                filtered[self.sr.id] = []
            if (label not in filtered[self.sr.id]):
                filtered[self.sr.id].append(label)
            settings.user.ui.Set(('filteredColumns_%s' % VERSION), filtered)
            self.OnColumnChanged(None)
            self.OnNewHeaders()



    def ShowColumn(self, label):
        if self.sr.id:
            filtered = settings.user.ui.Get(('filteredColumns_%s' % VERSION), {})
            if ((self.sr.id in filtered) and (label in filtered[self.sr.id])):
                filtered[self.sr.id].remove(label)
            filteredByDefault = settings.user.ui.Get(('filteredColumnsByDefault_%s' % VERSION), {})
            if ((self.sr.id in filteredByDefault) and (label in filteredByDefault[self.sr.id])):
                filteredByDefault[self.sr.id].remove(label)
                settings.user.ui.Set(('filteredColumnsByDefault_%s' % VERSION), filteredByDefault)
            settings.user.ui.Set(('filteredColumns_%s' % VERSION), filtered)
            self.OnColumnChanged(None)
            self.OnNewHeaders()



    def HideTriangle(self, column):
        for each in self.sr.scrollHeaders.children:
            if not isinstance(each, uicls.ScrollColumnHeaderCore):
                continue
            if ((each.name == column) and each.sr.triangle):
                each.sr.triangle.state = uiconst.UI_HIDDEN




    def Sort(self, by = None, reversesort = 0, forceHilite = 0):
        if self.debug:
            log.LogInfo('vscroll', ((('Sort' + strx(by)) + ', ') + strx(reversesort)))
        if self.smartSort:
            columns = self.GetColumns()
            primary = self.GetPrimaryColumn()
            sortcolumns = columns[:]
            if (primary in columns):
                idx = columns.index(primary)
                sortcolumns = columns[idx:]
            if columns:
                sortData = []
                rm = []
                for node in self.sr.nodes:
                    nodeData = []
                    idx = 0
                    for header in columns:
                        if (header not in sortcolumns):
                            self.HideTriangle(header)
                            continue
                        if (idx in rm):
                            value = 0
                        else:
                            value = node.Get(('sort_%s' % header), None)
                            if (value is None):
                                log.LogWarn('Cannot find sortvalue for column ', header, ' in scroll ', self.sr.id)
                                rm.append(idx)
                                self.HideTriangle(header)
                                value = 0
                            else:
                                try:
                                    value = value.lower()
                                except:
                                    sys.exc_clear()
                        idx += 1
                        nodeData.append(value)

                    sortData.append([nodeData, node])

                sortOrder = [ (idx, self.GetSmartSortDirection(header)) for (idx, header,) in enumerate(sortcolumns) if (idx not in rm) ]
                sortData.sort(lambda x, y, sortOrder = sortOrder: uiutil.SmartCompare(x, y, sortOrder))
                self.sr.nodes = [ each[1] for each in sortData ]
                self.CheckTabStops('Sort')
                self.UpdateCombined(refreshIndex=1, fromWhere='Sort')
                self.UpdatePosition(fromWhere='Sort(Smart)')
        else:
            idx = None
            headers = self.GetColumns()
            if (by in headers):
                idx = headers.index(by)
            groups = []
            newOrder = []
            rm = []
            for node in self.sr.nodes:
                if node.isSub:
                    continue
                val = self.GetSortValue(by, node, idx)
                if issubclass(node.decoClass, uicls.SE_ListGroupCore):
                    node.subNodes = self.SortSublist(node.get('subNodes', []), by, idx, reversesort)
                    groups.append(node)
                    continue
                if self.subSortBy:
                    subSortVal = self.GetSortValue(self.subSortBy, node, idx)
                    val = (val,
                     subSortVal,
                     (node.get('label', '').lower() or node.get('text', '').lower()))
                else:
                    val = (val, (node.get('label', '').lower() or node.get('text', '').lower()))
                newOrder.append((val, node))

            newOrder = uiutil.SortListOfTuples(newOrder)
            if reversesort:
                newOrder.reverse()
            grps = []
            for group in groups:
                grps.append(group)
                grps.extend(group.get('subNodes', []))

            newOrder = (grps + newOrder)
            idx = 0
            for node in newOrder:
                if node.panel:
                    uiutil.SetOrder(node.panel, -1)
                node.idx = idx
                idx += 1

            self.sr.nodes = newOrder
            self.UpdatePosition(fromWhere='Sort')
            if ((self.sortBy != by) or forceHilite):
                self.HiliteSorted(by, reversesort)
                self.sortBy = by



    def SortSublist(self, nodes, by, idx, reversesort = 0, sublevel = 1):
        groups = []
        newOrder = []
        div = None
        for node in nodes:
            if (node.isSub and (node.get('sublevel', 0) > sublevel)):
                continue
            if getattr(node.decoClass, 'isDivider', 0):
                div = node
                continue
            if node.Get('subNodes', []):
                node.subNodes = self.SortSublist(node.get('subNodes', []), by, idx, reversesort, (sublevel + 1))
                groups.append((node.get('label', '').lower(), node))
                continue
            val = self.GetSortValue(by, node, idx)
            val = (val, (node.get('label', '') or node.get('text', '')))
            newOrder.append((val, node))

        newOrder = uiutil.SortListOfTuples(newOrder)
        groups = uiutil.SortListOfTuples(groups)
        if reversesort:
            groups.reverse()
            newOrder.reverse()
        if div:
            newOrder.append(div)
        grps = []
        for group in groups:
            grps.append(group)
            grps.extend(group.get('subNodes', []))

        return (grps + newOrder)



    def UpdateCombined(self, refreshIndex = 0, applyTabstops = 0, fromWhere = ''):
        if self.debug:
            log.LogInfo('vscroll', 'UpdateCombined')
        if self.destroyed:
            return 
        if refreshIndex:
            self.sr.fromTopByIdx = {}
            fromTop = 0
        applyTabstops = (applyTabstops and self.sr.tabs)
        clipperWidth = self.GetContentWidth()
        for (idx, node,) in enumerate(self.sr.nodes):
            if refreshIndex:
                self.sr.fromTopByIdx[idx] = fromTop
                fromTop += self.GetNodeHeight(node, clipperWidth)
                node.idx = idx
                if node.panel:
                    if node.name:
                        node.panel.name = node.name
                    else:
                        node.panel.name = ('entry_%s' % idx)
            if applyTabstops:
                node.tabs = self.sr.tabs
                self.ApplyTabstops(node, 'UpdateCombined')




    def RefreshIndex(self, fromWhere = None):
        self.UpdateCombined(refreshIndex=1, fromWhere='RefreshIndex')
        self.lastSelected = None



    def RefreshSort(self, forceHilite = 0):
        if self.debug:
            log.LogInfo('vscroll', 'RefreshSort')
        if self.smartSort:
            self.Sort()
        else:
            sortby = self.GetSortBy()
            if sortby:
                self.Sort(sortby, self.GetSortDirection(), forceHilite=forceHilite)



    def ChangeSortBy(self, by, *args):
        if self.debug:
            log.LogInfo('vscroll', 'ChangeSortBy')
        if self.smartSort:
            self.MakePrimary(by, 0)
            self.ToggleSmartSortDirection(by)
            for header in self.sr.scrollHeaders.children:
                if not isinstance(header, uicls.ScrollColumnHeaderCore):
                    continue
                sortdir = self.GetSmartSortDirection(header.sr.column)
                header.sr.sortdir = sortdir
                header.ShowSortDirection(sortdir)

            self.Sort()
        elif (self.sortBy == by):
            self.reversedSort = (not self.reversedSort)
        else:
            self.reversedSort = 0
        self.sortBy = by
        if self.sr.id:
            pr = settings.user.ui.Get(('scrollsortby_%s' % VERSION), {})
            pr[self.sr.id] = (self.sortBy, self.reversedSort)
            settings.user.ui.Set(('scrollsortby_%s' % VERSION), pr)
        self.RefreshSort(1)



    def ChangeColumnOrder(self, column, toIdx):
        if self.debug:
            log.LogInfo('vscroll', 'ChangeColumnOrder')
        if (self.sr.id and self.smartSort):
            all = settings.user.ui.Get(('columnOrder_%s' % VERSION), {})
            currentOrder = all.get(self.sr.id, self.sr.headers)[:]
            if (column in currentOrder):
                currentOrder.remove(column)
            currentOrder.insert(toIdx, column)
            all[self.sr.id] = currentOrder
            settings.user.ui.Set(('columnOrder_%s' % VERSION), all)
            self.OnColumnChanged(None)
            self.OnNewHeaders()



    def HiliteSorted(self, by, rev, *args):
        if self.debug:
            log.LogInfo('vscroll', 'HiliteSorted')
        for header in self.sr.scrollHeaders.children:
            if not isinstance(header, uicls.ScrollColumnHeaderCore):
                continue
            header.Deselect()
            if (self.hiliteSorted and (header.sr.column == by)):
                header.Select(rev)
                if not self.sr.ignoreTabTrimming:
                    if not self.sr.columnHilite:
                        self.sr.columnHilite = uicls.Fill(parent=self.sr.maincontainer, align=uiconst.RELATIVE, color=(1.0, 1.0, 1.0, 0.0625), pos=((header.left - 4),
                         0,
                         header.width,
                         1200))
                        self.sr.columnHilite.name = 'columnHilite'
                    self.sr.columnHilite.width = (header.width + 1)
                    (hl, ht, hw, hh,) = header.GetAbsolute()
                    (sl, st, sw, sh,) = self.GetAbsolute()
                    self.sr.columnHilite.left = ((hl - sl) - 2)
                    self.sr.columnHilite.state = uiconst.UI_DISABLED




    def Clear(self):
        if self.debug:
            log.LogInfo('vscroll', 'Clear')
        self.LoadContent()



    def LoadContent(self, fixedEntryHeight = None, contentList = [], sortby = None, reversesort = 0, headers = [], scrollTo = None, customColumnWidths = False, showScrollTop = False, noContentHint = '', ignoreSort = False, scrolltotop = False, keepPosition = False):
        if self.destroyed:
            return 
        if scrolltotop:
            scrollTo = 0.0
        elif ((scrollTo is None) or keepPosition):
            scrollTo = self.GetScrollProportion()
        self._loading = 1
        self._fixedEntryHeight = fixedEntryHeight
        self._customColumnWidths = customColumnWidths
        self._ignoreSort = ignoreSort
        wnd = uiutil.GetWindowAbove(self)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'ShowLoad'))):
            wnd.ShowLoad()
        if self.debug:
            log.LogInfo('vscroll', ('Load %s %s %s %s' % (len(contentList),
             sortby,
             reversesort,
             headers)))
        for each in self.sr.nodes:
            each.panel = None
            each.scroll = None

        self.sr.nodes = self.sr.entries = []
        uiutil.Flush(self.sr.content)
        self._position = 0
        self.sr.content.top = 0
        if showScrollTop:
            self.sr.scrollcontrols.state = uiconst.UI_NORMAL
        self.sortBy = sortby
        self.reversedSort = reversesort
        self.AddNodes(0, contentList, checkTabstops=0)
        if self.destroyed:
            return 
        if (noContentHint and (not contentList)):
            self.ShowHint(noContentHint)
            self._ScrollCore__LoadHeaders([])
        else:
            self.ShowHint()
            self._ScrollCore__LoadHeaders(headers)
        self.ScrollToProportion(scrollTo, 1)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad'))):
            wnd.HideLoad()
        self._loadedonce = 1
        self._loading = 0


    Load = LoadContent

    def LoadHeaders(self, headers):
        wnd = uiutil.GetWindowAbove(self)
        try:
            if self._ScrollCore__LoadHeaders(headers):
                self.OnColumnChanged(self.sr.tabs)

        finally:
            if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad'))):
                wnd.HideLoad()




    def __LoadHeaders(self, headers):
        self.sr.headers = headers
        self.CheckTabStops('__LoadHeaders')
        if ((self is None) or self.destroyed):
            return 
        else:
            if headers:
                self.UpdateSizes()
                if not self.smartSort:
                    sortby = self.GetSortBy()
                    reversesort = self.GetSortDirection()
                    if (len(self.sr.headers) and sortby):
                        if (sortby not in self.sr.headers):
                            sortby = self.sr.headers[0]
                        if not self._ignoreSort:
                            self.Sort(sortby, reversesort)
                    else:
                        self.RefreshIndex('__LoadHeaders')
                        if len(self.sr.nodes):
                            self.UpdatePosition(fromWhere='__LoadHeaders')
                else:
                    self.Sort()
            if (len(self.sr.nodes) or (not headers)):
                self.lastHeaders = headers
            return 1



    def ResetColumnWidths(self):
        for header in self.GetColumns():
            self.ResetColumnWidth(header)




    def ResetColumnWidth(self, header, onlyReset = 0):
        if self.debug:
            log.LogInfo('vscroll', 'ResetColumnWidth')
        if ((self.sr.id is None) or self.refreshingColumns):
            return 
        if not onlyReset:
            wnd = uiutil.GetWindowAbove(self)
            if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'ShowLoad'))):
                wnd.ShowLoad()
        self.refreshingColumns = 1
        if ((header not in self.sr.fixedColumns) and self.sr.id):
            headertab = [(header,
              self.headerFontSize,
              2,
              ((TABMARGIN + 8) + 20),
              1)]
        else:
            headertab = [(header,
              self.headerFontSize,
              2,
              0,
              1)]
        if (header in self.GetColumns()):
            idx = self.GetColumns().index(header)
            width = None
            if self._customColumnWidths:
                headerWidth = uicore.font.GetTextWidth(header, fontsize=self.headerFontSize, letterspace=0, uppercase=True)
                headerWidth += ((TABMARGIN + 8) + 20)
                normHeader = header.replace('<br>', ' ')
                width = max(([headerWidth] + [ node.GetColumnWidthFunction(None, node, normHeader) for node in self.sr.nodes if (node.get('GetColumnWidthFunction', None) is not None) ]))
            else:
                fontsize = 12
                letterspace = 0
                shift = 0
                strengir = []
                for node in self.sr.nodes:
                    tabs = (node.get('label', '') or node.get('text', '')).split('<t>')
                    if (len(tabs) <= idx):
                        continue
                    if (node.panel and node.panel.sr.label):
                        label = node.panel.sr.label
                        fontsize = label.fontsize
                        letterspace = label.letterspace
                        if (idx == 0):
                            shift = label.left
                    strengir.append((tabs[idx],
                     fontsize,
                     letterspace,
                     shift,
                     0))

                tabstops = uicore.font.MeasureTabstops((headertab + strengir))
                if len(tabstops):
                    width = max(MINCOLUMNWIDTH, tabstops[0])
            if (width is not None):
                current = settings.user.ui.Get(('columnWidths_%s' % VERSION), {})
                current.setdefault(self.sr.id, {})[header] = width
                settings.user.ui.Set(('columnWidths_%s' % VERSION), current)
                self.CheckTabStops('ResetColumnWidth')
        if ((not onlyReset) and (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad')))):
            wnd.HideLoad()
        self.refreshingColumns = 0



    def ApplyTabstops(self, node, f = ''):
        if (self.sr.ignoreTabTrimming or (not self.GetColumns())):
            return 
        if self.debug:
            log.LogInfo('vscroll', 'ApplyTabstops')
        if (node.tabs and uiutil.GetAttrs(node, 'panel', 'OnColumnResize')):
            cols = []
            last = 0
            for tab in node.tabs:
                cols.append((tab - last))
                last = tab

            cols[0] -= self.sr.maincontainer.left
            node.panel.OnColumnResize(cols)
        elif ((not node.tabs) or ((not node.panel) or (not node.panel.sr.label))):
            return 
        tabstops = node.tabs
        label = node.panel.sr.label
        subTract = label.left
        if isinstance(label, uicls.LabelCore):
            newtext = (node.label or node.text)
            if ((getattr(label, 'tabs', None) != tabstops) or (label.xShift != -subTract)):
                label.xShift = -subTract
                label.tabs = tabstops
                label.text = newtext
            elif (newtext != label.text):
                label.text = newtext
            return 
        log.LogWarn('ApplyTabstops only supports ulabel now')



    def CheckTabStops(self, fromWhere = None):
        headers = self.GetColumns()
        if self.debug:
            log.LogInfo('vscroll', ('CheckTabStops %s %s' % (headers, fromWhere)))
        headertabs = []
        if ((headers is not None) and len(headers)):
            headertabs = [('<t>'.join(headers),
              self.headerFontSize,
              2,
              (TABMARGIN + 2),
              1)]
        strengir = []
        fontsize = 12
        letterspace = 0
        shift = 0
        for node in self.sr.nodes:
            t = (node.get('label', '') or node.get('text', ''))
            if not t:
                continue
            if (node.panel and node.panel.sr.label):
                label = node.panel.sr.label
                fontsize = label.fontsize
                letterspace = label.letterspace
                shift = label.left
            strengir.append((t,
             fontsize,
             letterspace,
             shift,
             0))

        tabstops = uicore.font.MeasureTabstops((strengir + headertabs))
        if (self.sr.id and headers):
            userDefined = settings.user.ui.Get(('columnWidths_%s' % VERSION), {}).get(self.sr.id, {})
            i = 0
            total = 0
            former = 0
            for header in headers:
                if (header in self.sr.fixedColumns):
                    stopSize = self.sr.fixedColumns[header]
                else:
                    userSetWidth = (userDefined.get(header, None) or self.sr.defaultColumnWidth.get(header, None))
                    minColumnWidth = self.sr.minColumnWidth.get(header, MINCOLUMNWIDTH)
                    if (userSetWidth is not None):
                        stopSize = max(userSetWidth, minColumnWidth)
                    else:
                        stopSize = (tabstops[i] - former)
                        if (header in self.sr.maxDefaultColumns):
                            stopSize = min(self.sr.maxDefaultColumns.get(header, minColumnWidth), stopSize)
                total += stopSize
                former = tabstops[i]
                tabstops[i] = total
                i += 1

        if ((headers != self.lastDrawnColumns) or (tabstops != self.sr.tabs)):
            self.lastDrawnColumns = headers
            self.sr.tabs = tabstops
            self.RefreshHeaders(headers, tabstops)
            if not self.smartSort:
                self.HiliteSorted(self.GetSortBy(), self.GetSortDirection(), 'CheckTabStops')
            if not self._loading:
                self.OnColumnChanged(tabstops)
        if (headers != self.lastDrawnColumns):
            self.OnNewHeaders()
        self.sr.tabs = tabstops
        self.UpdateCombined(applyTabstops=1, fromWhere='CheckTabStops')
        return tabstops



    def AddNode(self, idx, node, isSub = 0):
        if self.debug:
            log.LogInfo('vscroll', 'AddNode', idx)
        if (idx == -1):
            idx = len(self.sr.nodes)
        node.panel = None
        node.open = 0
        node.idx = idx
        node.isSub = isSub
        node.scroll = self.sr.selfProxy
        node.selected = node.get('isSelected', 0)
        if node.get('PreLoadFunction', None):
            node.PreLoadFunction(node)
        if self.destroyed:
            return 
        else:
            self.sr.nodes.insert(idx, node)
            self.ReloadNode(node)
            return node



    def ReloadNode(self, node):
        if node.id:
            if node.get('subNodes', []):
                rm = node.subNodes
                node.subNodes = []
                node.open = 0
                self.RemoveNodes(rm)
            if ((node.Get('GetSubContent', None) is not None) and uicore.registry.GetListGroupOpenState(node.id, default=node.get('openByDefault', False))):
                subcontent = node.GetSubContent(node)
                if ((not node.Get('hideNoItem', False)) and (not len(subcontent))):
                    noItemText = node.get('noItemText', mls.UI_GENERIC_NOITEM)
                    subcontent.append(self.GetNoItemNode(text=noItemText, sublevel=(node.get('sublevel', 0) + 1)))
                if (self and (not self.dead)):
                    self.AddNodes((node.idx + 1), subcontent, node)
                    node.subNodes = subcontent
                    node.open = 1
                    return subcontent


    ReloadEntry = ReloadNode

    def RemoveNodesRaw(self, nodes):
        wnd = uiutil.GetWindowAbove(self)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'ShowLoad'))):
            wnd.ShowLoad()
        for node in nodes:
            if node.panel:
                self.RecyclePanel(node.panel, 'RemoveNodesRaw')
            if (node in self.sr.nodes):
                self.sr.nodes.remove(node)

        self.RefreshIndex('RemoveNodesRaw')
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad'))):
            wnd.HideLoad()



    def InsertNodesRaw(self, fromIdx, nodesData):
        wnd = uiutil.GetWindowAbove(self)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'ShowLoad'))):
            wnd.ShowLoad()
        if (fromIdx == -1):
            fromIdx = len(self.sr.nodes)
        addedNodes = []
        idx = fromIdx
        for data in nodesData:
            newnode = self.AddNode(idx, data)
            addedNodes.append(newnode)
            idx += 1

        self.RefreshIndex('InsertNodesRaw')
        listIndex = 0
        for (_listIndex, each,) in enumerate(self.sr.content.children):
            if (each.sr.node.idx == (fromIdx - 1)):
                listIndex = (_listIndex + 1)
                break

        cIdx = 0
        for node in addedNodes:
            self.AddPanel(node.idx, 'InsertNodesRaw', update=0, listIndex=(listIndex + cIdx))
            cIdx += 1

        self.UpdateSizes(fromWhere='InsertNodesRaw')
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad'))):
            wnd.HideLoad()
        return addedNodes



    def AddNodes(self, fromIdx, nodesData, parentNode = None, checkTabstops = 1, ignoreSort = 0):
        if self.debug:
            log.LogInfo('vscroll', 'AddNodes start')
        wnd = uiutil.GetWindowAbove(self)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'ShowLoad'))):
            wnd.ShowLoad()
        if (fromIdx == -1):
            fromIdx = len(self.sr.nodes)
        isSub = 0
        if parentNode:
            isSub = (parentNode.get('sublevel', 0) + 1)
        nodes = []
        idx = fromIdx
        try:
            for data in nodesData:
                newnode = self.AddNode(idx, data, isSub=isSub)
                if (newnode is None):
                    continue
                subs = self.CollectSubNodes(newnode, clear=0)
                idx = ((newnode.idx + 1) + len(subs))
                nodes.append(newnode)

        except AttributeError:
            return 
        if parentNode:
            parentNode.subNodes = nodes
        if checkTabstops:
            self.CheckTabStops('AddNodes')
        if self.destroyed:
            return 
        else:
            self.RefreshIndex('AddNodes')
            if self.destroyed:
                return 
            if (self.GetSortBy() and (not (self._ignoreSort or ignoreSort))):
                self.UpdateSizes()
                self.RefreshSort()
            else:
                self.UpdatePosition(updateSizes=1, fromWhere='AddNodes')
            if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad'))):
                wnd.HideLoad()
            if self.debug:
                log.LogInfo('vscroll', 'AddNodes done')
            return nodes


    AddEntries = AddNodes

    def RemoveNodes(self, nodes, update = 1):
        if self.debug:
            log.LogInfo('vscroll', 'RemoveNodes start')
        wnd = uiutil.GetWindowAbove(self)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'ShowLoad'))):
            wnd.ShowLoad()
        subs = []
        for node in nodes:
            subs.extend(self.CollectSubNodes(node))

        for nodeList in (nodes, subs):
            for node in nodeList:
                if node.panel:
                    self.RecyclePanel(node.panel, 'RemoveNodes')
                if (node in self.sr.nodes):
                    self.sr.nodes.remove(node)


        if update:
            self.RefreshIndex('RemoveNodes')
            self.UpdatePosition(fromWhere='RemoveNodes', updateSizes=1)
        if (wnd and ((not wnd.destroyed) and hasattr(wnd, 'HideLoad'))):
            wnd.HideLoad()
        if self.debug:
            log.LogInfo('vscroll', 'RemoveNodes done')


    RemoveEntries = RemoveNodes

    def CollectSubNodes(self, node, nodes = None, clear = 1):
        if (nodes is None):
            nodes = []
        inNodes = [ id(each) for each in nodes ]
        for subnode in node.get('subNodes', []):
            if (subnode is None):
                continue
            self.CollectSubNodes(subnode, nodes, clear)
            if (id(subnode) not in inNodes):
                nodes.append(subnode)

        if clear:
            node.subNodes = []
        return nodes



    def RecyclePanels(self, panels, fromWhere = None):
        if self.debug:
            log.LogInfo('vscroll', ('RecyclePanel %s %s' % (len(panels), fromWhere)))
        for panel in panels:
            self.RecyclePanel(panel, fromWhere)




    def RecyclePanel(self, panel, fromWhere = None):
        if (panel and (panel in self.sr.content.children)):
            if panel.destroyed:
                return 
            node = panel.sr.node
            if hasattr(panel, 'OnRecycle'):
                panel.OnRecycle()
            panel.sr.node = None
            panel.preRecycleStateItemID = getattr(panel, 'stateItemID', None)
            panel.stateItemID = None
            panel.state = uiconst.UI_HIDDEN
            if hasattr(panel, 'Unload'):
                panel.Unload()
            self.sr.content.children.remove(panel)
            if (node and (node.panel is panel)):
                node.panel = None
                if getattr(node.decoClass, 'USECACHE', True):
                    self.sr.cache.setdefault(node.decoClass, []).append(panel)
                else:
                    panel.Close()



    def AddPanel(self, idx, fromWhere = None, update = 1, listIndex = -1):
        if self.debug:
            log.LogInfo('vscroll', ('AddPanel %s %s' % (idx, fromWhere)))
        node = self.GetNode(idx)
        if not node:
            return 
        else:
            cache = self.sr.cache.setdefault(node.decoClass, [])
            if cache:
                w = cache.pop()
                w.sr.node = node
                w.state = uiconst.UI_DISABLED
                uiutil.Transplant(w, self.sr.content, listIndex)
                node.panel = w
            else:
                decoClass = node.decoClass
                if getattr(decoClass, '__resPath__', None):
                    w = decoClass(blue.os.LoadObject(decoClass.__resPath__))
                    w.SetAlign(uiconst.TOTOP)
                    self.sr.content.children.append(w)
                else:
                    w = decoClass(state=uiconst.UI_DISABLED, parent=self.sr.content, idx=listIndex, align=uiconst.TOTOP)
                w.sr.node = node
                node.panel = w
                if hasattr(w, 'Startup'):
                    w.Startup(self.sr.selfProxy)
            node.scroll = self.sr.selfProxy
            clipperWidth = self.GetContentWidth()
            self.GetNodeHeight(node, clipperWidth)
            if node.name:
                w.name = node.name
            else:
                w.name = ('entry_%s' % idx)
            w.Load(node)
            if ((node.panel is None) or node.panel.destroyed):
                return 
            node.panel.state = uiconst.UI_NORMAL
            if self.sr.tabs:
                node.tabs = self.sr.tabs
                self.ApplyTabstops(node, 'AddPanel')
            return node



    def GetNodeHeight(self, node, clipperWidth):
        func = node.GetHeightFunction
        newStyle = getattr(node.decoClass, 'GetDynamicHeight', None)
        allowDynamicResize = node.get('allowDynamicResize', True)
        if func:
            if ((not node.height) or (allowDynamicResize and (node._lastClipperWidth != clipperWidth))):
                node.height = apply(func, (None,
                 node,
                 clipperWidth))
                node._lastClipperWidth = clipperWidth
        elif newStyle:
            if ((not node.height) or (allowDynamicResize and (node._lastClipperWidth != clipperWidth))):
                node.height = newStyle(node, clipperWidth)
                node._lastClipperWidth = clipperWidth
        elif self._fixedEntryHeight:
            node.height = self._fixedEntryHeight
        else:
            node.height = getattr(node.decoClass, 'ENTRYHEIGHT', 18)
        if not node.height:
            if func:
                apply(func, (None,
                 node,
                 clipperWidth))
            else:
                node.height = getattr(node.decoClass, 'ENTRYHEIGHT', 18)
        if node.panel:
            node.panel.height = node.height
            node.panel.width = 0
            node.panel.top = 0
            node.panel.left = 0
        return node.height



    def UpdateSizes(self, fromWhere = None, again = 1):
        uicore.uilib.RecalcWindows(None, True)
        if (self.updatingSizes or ((self is None) or self.destroyed)):
            return 
        if self.debug:
            log.LogInfo('vscroll', ('UpdateSizes called from: %s' % fromWhere))
        self.updatingSizes = 1
        (clipperWidth, clipperHeight,) = self.GetContentParentSize()
        total = 0
        for node in self.sr.nodes:
            total += self.GetNodeHeight(node, clipperWidth)

        overlayTotal = 0
        for (overlay, attrs, x, y,) in (self.sr.overlays + self.sr.underlays):
            overlayTotal = max(overlayTotal, (attrs.top + attrs.height))

        self._totalHeight = max(overlayTotal, total)
        self._clipperHeight = clipperHeight
        self.scrollingRange = max(0, (self._totalHeight - clipperHeight))
        if (self.scrollingRange and self.scrollEnabled):
            self.sr.scrollcontrols.state = uiconst.UI_NORMAL
        else:
            self.sr.scrollcontrols.state = uiconst.UI_HIDDEN
        self.updatingSizes = 0
        newClipperWidth = self.GetContentWidth()
        if (again and (clipperWidth != newClipperWidth)):
            self.UpdateSizes('UpdateSizes', 0)



    def GetContentWidth(self):
        (l, t, w, h,) = self.sr.clipper.GetAbsolute()
        return w



    def GetContentHeight(self):
        return self._totalHeight


    GetTotalHeight = GetContentHeight

    def GetContentParentSize(self):
        (l, t, w, h,) = self.sr.clipper.GetAbsolute()
        return (w, h)



    def UpdatePositionThreaded(self, updateScrollHandle = 1, fromWhere = None, updateSizes = 0):
        try:
            self._UpdatePositionThreaded(updateScrollHandle, fromWhere, updateSizes)
        except TaskletExit:
            pass
        except Exception, e:
            raise 



    def _UpdatePositionThreaded(self, updateScrollHandle = 1, fromWhere = None, updateSizes = 0):
        if self.updatePosTasklet:
            self.updatePosTasklet.kill()
            self.updatePosTasklet = None
        self.updatePosTasklet = uthread.new(self.UpdatePosition, updateScrollHandle, fromWhere, updateSizes)



    def UpdatePosition(self, updateScrollHandle = 1, fromWhere = None, updateSizes = 0):
        if self.destroyed:
            return 
        if self.debug:
            log.LogInfo('vscroll', ('UpdatePosition from %s' % fromWhere))
        atBottom = (self._position and (self._position == self.scrollingRange))
        if updateSizes:
            self.UpdateSizes(fromWhere='UpdatePosition')
        if self.debug:
            log.LogInfo('vscroll', ('UpdatePosition called from: %s' % (fromWhere)))
        if ((not self.scrollingRange) or (atBottom or self.stickToBottom)):
            self._position = self.scrollingRange
        self._position = min(self._position, self.scrollingRange)
        self.CheckNodes()
        if updateScrollHandle:
            self.UpdateScrollHandle(fromWhere='UpdatePosition')
        self.CheckOverlaysAndUnderlays()
        self.OnUpdatePosition(self)
        self.updatePosTasklet = None



    def OnChar(self, enteredChar, *args):
        if (enteredChar < 32):
            return False
        else:
            if not self.sr.nodes:
                return True
            if (((blue.os.TimeAsDouble() - self.lastCharReceivedAt) < 1.0) and (self.currChars is not None)):
                self.currChars += unichr(enteredChar).lower()
            else:
                self.currChars = unichr(enteredChar).lower()
            if (enteredChar == uiconst.VK_SPACE):
                selected = self.GetSelected()
                if ((len(selected) == 1) and ((self.currChars == ' ') and (uiutil.GetAttrs(selected[0], 'panel', 'OnCharSpace') is not None))):
                    selected[0].panel.OnCharSpace(enteredChar)
                    return True
            uthread.new(self._OnCharThread, enteredChar)
            self.lastCharReceivedAt = blue.os.TimeAsDouble()
            return True



    def _OnCharThread(self, enteredChar):
        if self.dead:
            return 
        charsBefore = self.currChars
        blue.pyos.synchro.Sleep(100)
        if self.dead:
            return 
        if (self.currChars != charsBefore):
            return 
        selected = self.GetSelected()
        if not selected:
            selected = self.sr.nodes
        selected = selected[0]
        numEntries = len(self.sr.nodes)
        if (selected not in self.sr.nodes):
            return 
        startIndex = self.sr.nodes.index(selected)
        if (len(self.currChars) == 1):
            startIndex += 1
        entryRange = (range(numEntries)[startIndex:] + range(numEntries)[:startIndex])
        for i in entryRange:
            entry = self.sr.nodes[i]
            if (entry.charIndex and entry.charIndex.startswith(self.currChars)):
                self.SelectNode(entry)
                entryPos = self.sr.nodes.index(entry)
                self.ScrollToProportion((float(entryPos) / numEntries), updateScrollHandle=True)
                break




    def OnDelete(self):
        return 



    def OnUpdatePosition(self, *args):
        return 



    def CheckNodes(self):
        if self.sr.nodes:
            (startIndex, fromTop,) = self.GetStartIdx()
            self.sr.content.top = int((-self._position + fromTop))
            self.CheckOverlaysAndUnderlays()
            rem = [ panel for panel in self.sr.content.children if ((panel.sr.node is None) or ((panel.sr.node.idx is None) or (panel.sr.node.idx < startIndex))) ]
            if len(rem):
                self.RecyclePanels(rem, 'remove from above')
            (cl, ct, cw, ch,) = self.sr.clipper.GetAbsolute()
            maxHeight = (self._position + ch)
            if len(self.sr.content.children):
                listIndex = 0
                totalHeight = fromTop
                for idx in xrange(startIndex, self.sr.content.children[0].sr.node.idx):
                    node = self.AddPanel(idx, 'start', update=0, listIndex=listIndex)
                    totalHeight += node.panel.height
                    listIndex += 1
                    if (totalHeight >= maxHeight):
                        break

            idx = startIndex
            totalHeight = fromTop
            rem = []
            for each in self.sr.content.children:
                if (totalHeight <= maxHeight):
                    if (each.sr.node.idx != idx):
                        if self.debug:
                            log.LogInfo('vscroll', ('CheckNodes IndexError %s' % idx))
                        rem.append(each)
                        continue
                    totalHeight += each.height
                    idx += 1
                elif self.debug:
                    log.LogInfo('vscroll', ('Exceeding maxheight %s' % idx))
                rem.append(each)

            if rem:
                self.RecyclePanels(rem, 'indexError or tomany')
            lenNodes = len(self.sr.nodes)
            while ((idx < lenNodes) and (totalHeight < maxHeight)):
                node = self.AddPanel(idx, 'end', update=0)
                if not node:
                    break
                totalHeight += node.panel.height
                idx += 1




    def CheckOverlaysAndUnderlays(self):
        for (overlay, attrs, x, y,) in (self.sr.overlays + self.sr.underlays):
            if ((overlay is None) or overlay.destroyed):
                continue
            overlay.top = (attrs.top - self._position)
            if (attrs.Get('align', None) == 'right'):
                overlay.left = ((self.GetContentWidth() - overlay.width) - attrs.left)
            overlay.SetAlign(uiconst.RELATIVE)
            overlay.state = uiconst.UI_NORMAL
            visible = self.OverlayIsVisible(overlay)
            if (visible and (not overlay.loaded)):
                overlay.Load()
            else:
                if ((not visible) and overlay.loaded):
                    overlay.Unload()




    def OverlayIsVisible(self, overlay):
        (ol, ot, ow, oh,) = overlay.GetAbsolute()
        (cl, ct, cw, ch,) = self.GetAbsolute()
        if (((ot + oh) > ct) and (ot < (ct + ch))):
            return 1
        else:
            return 0



    def GetHeightFromStart(self, tryRefresh = 1):
        topIdx = 0
        if (len(self.sr.content.children) and hasattr(self.sr.content.children[0], 'sr')):
            topIdx = self.sr.content.children[0].sr.node.idx
        if (topIdx == 0):
            return 0
        else:
            if (topIdx in self.sr.fromTopByIdx):
                return self.sr.fromTopByIdx[topIdx]
            if ((topIdx - 1) in self.sr.fromTopByIdx):
                clipperWidth = self.GetContentWidth()
                self.sr.fromTopByIdx[topIdx] = (self.sr.fromTopByIdx[(topIdx - 1)] + self.GetNodeHeight(self.sr.nodes[topIdx], clipperWidth))
                return self.sr.fromTopByIdx[topIdx]
            if tryRefresh:
                self.RefreshIndex('GetHeightFromStart')
                return self.GetHeightFromStart(0)
            return 0



    def GetHeightToIdx(self, idx, tryRefresh = 1):
        if (idx in self.sr.fromTopByIdx):
            return self.sr.fromTopByIdx[idx]
        else:
            if tryRefresh:
                self.RefreshIndex('GetHeightToIdx')
                return self.GetHeightToIdx(idx, 0)
            return 0



    def GetStartIdx(self):
        clipperWidth = self.GetContentWidth()
        fromTop = 0
        i = 0
        for node in self.sr.nodes:
            if fromTop <= self._position < (fromTop + self.GetNodeHeight(node, clipperWidth)):
                return (min((len(self.sr.nodes) - 1), i), fromTop)
            fromTop += node.height
            i += 1

        return (i, fromTop)



    def GetNode(self, idx, checkInternalNodes = False):
        if checkInternalNodes:
            allNodes = self.GetNodes(allowNone=True)
        else:
            allNodes = self.sr.nodes
        if (idx == -1):
            if allNodes:
                return allNodes[-1]
            else:
                return 
        if (len(allNodes) > idx):
            return allNodes[idx]



    def _OnKeyDown(self, key, flag):
        if (uiconst.VK_DELETE == key):
            self.OnDelete()



    def _OnResize(self, *args, **kw):
        uicls.Container._OnResize(self, *args, **kw)
        if getattr(self, '_loadedonce', False):
            self.UpdatePositionThreaded(fromWhere='_OnResize', updateSizes=1)
        self.Resizing()



    def OnClipperResize(self, *args, **kw):
        return 



    def Resizing(self):
        return 



    def BrowseNodes(self, up):
        sel = self.GetSelected()
        control = uicore.uilib.Key(uiconst.VK_CONTROL)
        shift = uicore.uilib.Key(uiconst.VK_SHIFT)
        if sel:
            shiftIdx = None
            if ((not control) and shift):
                r = [ node.idx for node in sel ]
                if up:
                    if (r[0] < self.lastSelected):
                        shiftIdx = (r[0] - 1)
                    else:
                        shiftIdx = (r[-1] - 1)
                else:
                    if (r[0] < self.lastSelected):
                        shiftIdx = (r[0] + 1)
                shiftIdx = (r[-1] + 1)
            if (shiftIdx is None):
                if (len(sel) > 1):
                    idx = sel[[-1, 0][up]].idx
                else:
                    idx = sel[-1].idx
                idx += [1, -1][up]
            else:
                idx = shiftIdx
            total = len(self.GetNodes())
            if 0 <= idx < total:
                self.ActivateIdx(idx)
                return 1
            return 0


    BrowseEntries = BrowseNodes

    def OnUp(self):
        if not self.GetSelected():
            self.ActivateIdx((len(self.sr.entries) - 1))
            return 
        if not self.BrowseNodes(1):
            self.Scroll((1 + (10 * uicore.uilib.Key(uiconst.VK_SHIFT))))



    def OnDown(self):
        if not self.GetSelected():
            self.ActivateIdx(0)
            return 
        if not self.BrowseNodes(0):
            self.Scroll((-1 - (10 * uicore.uilib.Key(uiconst.VK_SHIFT))))



    def OnHome(self):
        self.ScrollToProportion(0.0, 1)



    def OnEnd(self):
        self.ScrollToProportion(1.0, 1)



    def _OnMouseWheel(self, *etc):
        if getattr(self, 'wheeling', 0):
            return 1
        else:
            self.wheeling = 1
            self.Scroll((uicore.uilib.dz / 240.0))
            self.wheeling = 0
            return 1



    def Scroll(self, dz):
        if self.debug:
            log.LogInfo('vscroll', ('Scroll %s' % (dz)))
        step = 37
        pos = max(0, min(self.scrollingRange, (self._position - (step * dz))))
        if (pos != self._position):
            self._position = int(pos)
            self.stickToBottom = False
            self.UpdatePositionThreaded(fromWhere='Scroll')



    def GetScrollProportion(self):
        if self.scrollingRange:
            return (self._position / float(self.scrollingRange))
        else:
            return 0.0



    def UpdateScrollHandle(self, fromWhere = ''):
        if ((self is None) or (self.destroyed or (not self.sr.scrollcontrols))):
            return 
        if (self.scrollingRange and (self._clipperHeight and self._totalHeight)):
            self.sr.scrollcontrols.SetScrollHandleSize(max(0.0, min(1.0, (float(self._clipperHeight) / self._totalHeight))))
        else:
            self.sr.scrollcontrols.SetScrollHandleSize(1.0)
        if (self._position and self.scrollingRange):
            self.sr.scrollcontrols.SetScrollHandlePos((self._position / float(self.scrollingRange)))
        else:
            self.sr.scrollcontrols.SetScrollHandlePos(0.0)



    def ScrollToProportion(self, proportion, updateScrollHandle = 0):
        proportion = min(1.0, max(0.0, proportion))
        pos = int(max(0, (self.scrollingRange * proportion)))
        if (pos != self._position):
            self._position = int(pos)
            self.UpdatePositionThreaded(updateScrollHandle=updateScrollHandle, fromWhere='ScrollToPorportion')



    def GetMinSize(self):
        return (64, 64)



    def GetNoItemNode(self, text, sublevel = 0, *args):
        return uicls.ScrollEntryNode(label=text, sublevel=sublevel)




class ScrollControlsCore(uicls.Container):
    __guid__ = 'uicls.ScrollControlsCore'

    def ApplyAttributes(self, attributes):
        uicls.Container.ApplyAttributes(self, attributes)
        self.sr.scrollhandle = None
        self.Prepare_()



    def Prepare_(self):
        uicls.Line(parent=self, align=uiconst.TOLEFT, color=(1.0, 1.0, 1.0, 0.125))
        self.sr.underlay = uicls.Frame(name='__underlay', color=(0.0, 0.0, 0.0, 0.5), frameConst=uiconst.FRAME_FILLED_CORNER0, parent=self)
        self.Prepare_ScrollHandle_()



    def Prepare_ScrollHandle_(self):
        subparent = uicls.Container(name='subparent', parent=self, align=uiconst.TOALL, padding=(-1, 0, 0, 0))
        self.sr.scrollhandle = uicls.ScrollHandle(name='__scrollhandle', parent=subparent, align=uiconst.TOPLEFT, pos=(0, 0, 2, 2), state=uiconst.UI_NORMAL, idx=0)



    def Startup(self, dad):
        self.sr.dad = dad
        self.sr.scrollhandle.Startup(dad)



    def _OnMouseDown(self, w, *args):
        scrollTop = self.sr.scrollhandle
        (l, t, w, h,) = self.GetAbsolute()
        absTop = ((t + scrollTop.width) + (scrollTop.height / 2))
        absBottom = (((t + h) - scrollTop.width) - (scrollTop.height / 2))
        proportion = ((uicore.uilib.y - absTop) / float((absBottom - absTop)))
        proportion = min(1.0, max(0.0, proportion))
        self.sr.dad.stickToBottom = 0
        self.sr.dad.ScrollToProportion(proportion, 1)



    def SetScrollHandleSize(self, sizeFraction):
        if self.sr.scrollhandle:
            self.sr.scrollhandle.UpdateSize(sizeFraction)



    def SetScrollHandlePos(self, posFraction):
        if self.sr.scrollhandle:
            self.sr.scrollhandle.ScrollToProportion(posFraction)




class ScrollHandleCore(uicls.Container):
    __guid__ = 'uicls.ScrollHandleCore'

    def ApplyAttributes(self, attributes):
        uicls.Container.ApplyAttributes(self, attributes)
        self.sr.hilite = None
        self.sr.dad = None
        self._dragging = False
        self.Prepare_()



    def Prepare_(self):
        uicls.Fill(name='__activeframe', color=(1.0, 1.0, 1.0, 0.25), parent=self, padding=(2, 1, 1, 1))
        self.Prepare_Hilite_()



    def Prepare_Hilite_(self):
        self.sr.hilite = uicls.Fill(parent=self, color=(1.0, 1.0, 1.0, 0.5), padding=(2, 1, 1, 1), state=uiconst.UI_HIDDEN)



    def Startup(self, dad):
        self.sr.dad = dad



    def UpdateSize(self, sizeportion):
        (pl, pt, pw, ph,) = self.parent.GetAbsolute()
        self.height = max(16, int((ph * sizeportion)))
        self.width = (pw - (self.left * 2))



    def ScrollToProportion(self, proportion):
        proportion = min(1.0, max(0.0, proportion))
        (pl, pt, pw, ph,) = self.parent.GetAbsolute()
        self.top = int(((ph - self.height) * proportion))



    def _OnMouseDown(self, btn, *args):
        if (btn != uiconst.MOUSELEFT):
            return 
        self.startdragdata = (uicore.uilib.y, self.top)
        self._dragging = 1
        self.top = (self.top + 1)
        if self.sr.dad:
            self.sr.dad.sr.content.state = uiconst.UI_DISABLED
            self.sr.dad.stickToBottom = 0



    def _OnMouseMove(self, *etc):
        self.MouseMove()



    def MouseMove(self, *args):
        if not self._dragging:
            return 
        if not uicore.uilib.leftbtn:
            self._dragging = 0
            return 
        (pl, pt, pw, ph,) = self.parent.GetAbsolute()
        (y0, top0,) = self.startdragdata
        range_ = (ph - self.height)
        self.top = max(0, min(range_, ((top0 - y0) + uicore.uilib.y)))
        scrollTo = 0.0
        if (range_ and self.top):
            scrollTo = (self.top / float(range_))
        if self.sr.dad:
            self.sr.dad.ScrollToProportion(scrollTo)
            self.sr.dad.UpdateScrollHandle(fromWhere='MouseMove')



    def _OnMouseUp(self, btn, *args):
        if (btn == uiconst.MOUSELEFT):
            self._dragging = 0
        self.top = (self.top - 1)
        uicore.uilib.RecalcWindows()
        if self.sr.dad:
            self.sr.dad.sr.content.state = uiconst.UI_NORMAL
            self.sr.dad.UpdatePositionThreaded(fromWhere='_OnMouseUp')



    def _OnMouseEnter(self, *args):
        if self.sr.hilite:
            self.sr.hilite.state = uiconst.UI_DISABLED



    def _OnMouseExit(self, *args):
        if self.sr.hilite:
            self.sr.hilite.state = uiconst.UI_HIDDEN




class ScrollBtnCore(uicls.Container):
    __guid__ = 'uicls.ScrollBtnCore'

    def Startup(self, dad, direction):
        self.wannascroll = 0
        self.sr.dad = dad
        self.sr.direction = direction



    def _OnMouseEnter(self, *args):
        if self.sr.hilite:
            self.sr.hilite.state = uiutil.UI_DISABLED



    def _OnMouseExit(self, *args):
        if self.sr.hilite:
            self.sr.hilite.state = uiutil.UI_HIDDEN



    def _OnMouseDown(self, *args):
        self.wannascroll = 1
        self.sr.dad.stickToBottom = 0
        uthread.pool('ScrollBtn::_OnMouseDown-->Scroll', self.Scroll)



    def _OnMouseUp(self, *args):
        self.wannascroll = 0
        self.children[0].top = [1, -1][(self.sr.direction < 0)]



    def Scroll(self):
        while self.wannascroll:
            self.sr.dad.Scroll(self.sr.direction)
            self.children[0].top = [2, 0][(self.sr.direction < 0)]
            blue.pyos.synchro.Sleep(100)





class ColumnHeaderCore(uicls.Container):
    __guid__ = 'uicls.ScrollColumnHeaderCore'
    headerFontSize = 10
    letterspace = 1

    def ApplyAttributes(self, attributes):
        uicls.Container.ApplyAttributes(self, attributes)
        self.Prepare_Divider_()
        self.Prepare_Label_()
        self.sr.label.text = attributes.label
        self.draggingAllowed = 0
        self.sr.triangle = None
        self.sr.selection = None



    def Prepare_Divider_(self):
        uicls.Line(parent=self, align=uiconst.TORIGHT, color=(1.0, 1.0, 1.0, 0.25))



    def Prepare_Label_(self):
        textclipper = uicls.Container(name='textclipper', parent=self, align=uiconst.TOALL, padding=(6, 2, 6, 0), state=uiconst.UI_PICKCHILDREN, clipChildren=1)
        self.sr.label = uicls.Label(text='', parent=textclipper, letterspace=self.letterspace, fontsize=self.headerFontSize, hilightable=1, state=uiconst.UI_DISABLED, uppercase=1, autowidth=1, autoheight=1)



    def _OnClose(self):
        uicls.Container._OnClose(self)
        if self.sr.selection:
            s = self.sr.selection
            self.sr.selection = None
            s.Close()
        uicls.Container._OnClose(self)



    def _OnEndDrag(self, *args):
        if self.sr.frame:
            f = self.sr.frame
            self.sr.frame = None
            f.Close()
        for each in self.children:
            if (each.name == 'scaler'):
                each.state = uiconst.UI_NORMAL

        if hasattr(uicore.uilib.mouseOver, 'OnDropColumn'):
            uicore.uilib.mouseOver.OnDropColumn(self)



    def _OnStartDrag(self, *args):
        for each in self.children:
            if (each.name == 'scaler'):
                each.state = uiconst.UI_HIDDEN

        self.sr.frame = uicls.Frame(parent=self, idx=0)



    def OnDropColumn(self, droppings):
        if (droppings == self):
            return 
        (l, t, w, h,) = self.GetAbsolute()
        if (uicore.uilib.x < (l + (w / 2))):
            newIdx = self.sr.idx
        else:
            newIdx = (self.sr.idx + 1)
        self.wr.scroll.ChangeColumnOrder(droppings.sr.column, newIdx)



    def _OnDblClick(self, *args):
        self.wr.scroll.ResetColumnWidth(self.sr.column)



    def _OnClick(self, *args):
        if (self.sr.sortdir is not None):
            self.wr.scroll.ChangeSortBy(self.sr.header)



    def GetMenu(self, *args):
        return self.wr.scroll.GetHeaderMenu(self.sr.column)



    def Deselect(self):
        if self.sr.triangle:
            self.sr.triangle.state = uiconst.UI_HIDDEN
        if self.sr.selection:
            self.sr.selection.state = uiconst.UI_HIDDEN



    def Select(self, rev, *args):
        self.ShowSortDirection([1, -1][rev])



    def ShowSortDirection(self, direction):
        if (direction is None):
            return 
        if not self.sr.triangle:
            self.sr.triangle = uicls.Icon(align=uiconst.CENTERRIGHT, pos=(3, 0, 16, 16), parent=self, idx=0, name='directionIcon', icon='uicore_1_16_16')
        self.sr.triangle.state = uiconst.UI_DISABLED
        if (direction == 1):
            uiutil.MapIcon(self.sr.triangle, 'uicore_1_16_16')
        else:
            uiutil.MapIcon(self.sr.triangle, 'uicore_1_16_15')




